//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Xml;

namespace OpenLS.Vml.Parsing
{
    /// <summary>
    /// This class models an XML node, an array of elements in scope is maintained while parsing
    /// for validation purposes, and these Node objects are reused to reduce object allocation,
    /// hence the reset method.  
    /// </summary>
    internal class Node
    {
        private readonly HWStack<Attribute> _attributes = new HWStack<Attribute>(10);
        internal SgmlReaderState _currentState;
        internal bool _isEmpty;
        private QualifiedName _qualifiedName;
        internal XmlNodeType _nodeType;
        internal bool _simulated; // tag was injected into result stream.
        internal XmlSpace _space;
        private string _value;
        internal string _xmlLang;

        public int AttributeCount
        {
            get { return _attributes.Count; }
        }

        public string LocalName
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() != null);
                Contract.Ensures(!Contract.Result<string>().Contains(":"));
                return _qualifiedName.LocalName;
            }
        }
        public string Prefix
        {
            get
            {
                return _qualifiedName.Prefix;
            }
        }

        public QualifiedName Name
        {
            get { return _qualifiedName; }
            set { throw new NotImplementedException(); }
        }

        public string Value
        {
            get {
                return _value;
            }
            set {
                _value = value;
            }
        }

        public string GetNamespaceUri (IEnumerable<Node> nodes)
        {
             {
                 string prefix = this.Prefix;
                 foreach (var node in nodes)
                 {
                     string s = node.GetNamespaceUri(prefix);
                     if (s != null)
                         return s;
                 }
                 return string.Empty;
            }
        }

        private string GetNamespaceUri(string prefix)
        {
            if (prefix == null)
            {
                foreach (Attribute attribute in getAttributes())
                {
                    if (attribute.LocalName == "xmlns")
                        return attribute.Value;
                }
            }
            else
            foreach (Attribute attribute in getAttributes())
            {
                if (attribute.LocalName == prefix && attribute.Prefix == "xmlns")
                    return attribute.Value;
            }

            return null;
        }

        private IEnumerable<Attribute> getAttributes()
        {
            int count = this.AttributeCount;
            for (int i = 0; i < AttributeCount; i++)
                yield return this.GetAttribute(i);
        }

#if DEBUG
        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            
        }
#endif


        /// <summary>
        /// Attribute objects are reused during parsing to reduce memory allocations, 
        /// hence the Reset method. 
        /// </summary>
        public void Reset(QualifiedName fullName, XmlNodeType nt, string value)
        {
            _value = value;

            _qualifiedName =fullName;
            _nodeType = nt;
            _space = XmlSpace.None;
            _xmlLang = null;
            _isEmpty = true;
            _attributes.Count = 0;
#if SGML
            Debug.WriteLine("value = " + _value + " _qualifiedname = " + _qualifiedName);
#endif
        }
        public Attribute AddAttribute(QualifiedName name, string value, char quotechar, bool caseInsensitive)
        {
            Attribute a;
            // check for duplicates!
            for (int i = 0, n = _attributes.Count; i < n; i++)
            {
                a = (Attribute) _attributes[i];
                if (caseInsensitive && a.Name.Equals(name, true) )
                {
                    return null;
                }
                if (a.Name.Equals(name))
                    return null;
//                if ((object) a.LocalName == (object) name)
                {
  //                  return null;
                }
            }
            // This code makes use of the high water mark for attribute objects,
            // and reuses exisint Attribute objects to avoid memory allocation.
            a = (Attribute) _attributes.Push();
            if (a == null)
            {
                a = new Attribute();
                _attributes[_attributes.Count - 1] = a;
            }
            a.Reset(name, value, quotechar);
            return a;
        }

        public void CopyAttributes(Node n)
        {
            for (int i = 0, len = n._attributes.Count; i < len; i++)
            {
                var a = (Attribute) n._attributes[i];
                AddAttribute(a.Name, a.Value, a.QuoteChar, false);
            }
        }

        public int GetAttribute(string name)
        {
            for (int i = 0, n = _attributes.Count; i < n; i++)
            {
                var a = (Attribute) _attributes[i];
                if (a.LocalName == name)
                {
                    return i;
                }
            }
            return -1;
        }

        public Attribute GetAttribute(int i)
        {
            if (i >= 0 && i < _attributes.Count)
            {
                var a = (Attribute) _attributes[i];
                return a;
            }
            return null;
        }
    }
}